package classpath

import (
	"archive/zip"
	"errors"
	"io"
	"os"
	"path/filepath"
	"strings"
)

const pathListSeparator = string(os.PathListSeparator)

// 类路径
type Entry interface {
	// 负责寻找 和读取class 文件
	ReadClass(className string) ([]byte, Entry, error)
	// 返回变量的字符串解释
	String() string
}

func NewEntry(path string) Entry {
	if strings.Contains(path, pathListSeparator) {
		return NewCompositeEntry(path)
	}

	if strings.HasSuffix(path, "*") {
		return NewWildcardEntry(path)
	}
	//jar或zip
	if strings.HasSuffix(path, ".jar") ||
		strings.HasSuffix(path, ".zip") ||
		strings.HasSuffix(path, ".JAR") ||
		strings.HasSuffix(path, ".ZIP") {
		return NewZipEntry(path)
	}
	//目录
	return NewDirEntry(path)
}

func NewDirEntry(path string) Entry {
	//将path参数转化成绝对路径
	absDir, err := filepath.Abs(path)
	//出错则终止运行
	if err != nil {
		panic(err)
	}
	return &DirEntry{AbsDir_: absDir}
}

func NewZipEntry(path string) Entry {
	//将path参数转化成绝对路径
	absPath, err := filepath.Abs(path)
	//出错则终止运行
	if err != nil {
		panic(err)
	}
	return &ZipEntry{AbsPath_: absPath}
}

func NewWildcardEntry(path string) *CompositeEntry {
	baseDir := path[:len(path)-1] //删除 *
	compositeEntry := []Entry{}
	walkFn := func(path string, info os.FileInfo, err error) error {
		if err != nil {
			return err
		}
		//不匹配路径，跳过
		if info.IsDir() && path != baseDir {
			return filepath.SkipDir
		}
		//筛选出jar文件
		if strings.HasSuffix(path, ".jar") || strings.HasSuffix(path, "JAR") {
			jarEntry := NewZipEntry(path)
			compositeEntry = append(compositeEntry, jarEntry)
		}
		return nil
	}
	filepath.Walk(baseDir, walkFn)
	return (*CompositeEntry)(&compositeEntry)
}

func NewCompositeEntry(pathList string) *CompositeEntry {
	compositeEntry := []Entry{}
	//通过分隔符，一个一个类路径进行解析
	for _, path := range strings.Split(pathList, pathListSeparator) {
		entry := NewEntry(path)
		compositeEntry = append(compositeEntry, entry)
	}
	return (*CompositeEntry)(&compositeEntry)
}

type ZipEntry struct {
	// 绝对路径
	AbsPath_ string
}

func (self *ZipEntry) ReadClass(className string) ([]byte, Entry, error) {
	//读取封包
	r, err := zip.OpenReader(self.AbsPath_)
	if err != nil {
		return nil, nil, err
	}
	defer r.Close()

	//找到指定类文件
	for _, f := range r.File {
		if f.Name == className {
			rc, err := f.Open()
			if err != nil {
				return nil, nil, err
			}
			defer rc.Close()

			//返回二进制数据
			data, err := io.ReadAll(rc)
			if err != nil {
				return nil, nil, err
			}
			return data, self, nil
		}
	}
	//类文件未找到，抛出错误
	return nil, nil, errors.New("class Not Found：" + className)
}

func (self *ZipEntry) String() string {
	return self.AbsPath_
}

type DirEntry struct {
	AbsDir_ string
}

func (self *DirEntry) ReadClass(className string) ([]byte, Entry, error) {
	//拼接类的绝对路径
	fileName := filepath.Join(self.AbsDir_, className)
	//读取二进制文件流并返回
	data, err := os.ReadFile(fileName)
	return data, self, err
}

func (self *DirEntry) String() string {
	return self.AbsDir_
}

type CompositeEntry []Entry

func (self *CompositeEntry) ReadClass(className string) ([]byte, Entry, error) {
	//调用各自的readClass方法，获取class文件的二进制数据
	for _, entry := range *self {
		data, from, err := entry.ReadClass(className)
		if err == nil {
			return data, from, err
		}
	}
	//没有读到，抛出异常
	return nil, nil, errors.New("class not found: " + className)
}

func (self *CompositeEntry) String() string {
	strs := make([]string, len(*self))

	//拼接各自是String方法
	for i, entry := range *self {
		strs[i] = entry.String()
	}
	return strings.Join(strs, pathListSeparator)
}
